#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 ZHENG Zhong <http://www.zhengzhong.net/>
#
# Created on 2008-04-28.
# $Id$
#

import logging
import urlparse

from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.api import images

#---------------------------------------------------------------------------------------------------

def _strip_string(value, default_value=None):
    if not value or not value.strip():
        value = default_value
    else:
        value = value.strip()
    return value

#---------------------------------------------------------------------------------------------------

class HotMovie(db.Model):

    name = db.StringProperty(required=True)
    abc = db.StringListProperty(default=[])
    description = db.TextProperty()
    imdb_id = db.StringProperty(required=True)
    run_time = db.IntegerProperty(default=0)
    poster_type = db.StringProperty()
    poster_data = db.BlobProperty()
    hot_scenes = db.IntegerProperty(default=0)
    hot_seconds = db.IntegerProperty(default=0)
    submitter = db.UserProperty(required=True)
    submit_date = db.DateTimeProperty(auto_now_add=True)
    
    def __str__(self):
        return str(self.name)
    
    def __unicode__(self):
        return unicode(self.name)
    
    def has_poster(self):
        return bool(self.poster_type and self.poster_data)
    
    def hot_ratio_per_mille(self):
        if self.run_time > 0:
            return 1000 * self.hot_seconds // (self.run_time * 60)
        else:
            return 0
    
    def put(self):
        # Ensure that movie name is not empty.
        if not self.name or not self.name.strip():
            raise ValueError('Movie name is required.')
        else:
            self.name = self.name.strip()
        # Strip spaces for movie description.
        self.description = _strip_string(self.description)
        # Ensure that movie's IMDb ID is valid.
        if not self.imdb_id or not self.imdb_id.strip():
            raise ValueError('Movie IMDb ID is required.')
        elif not self.imdb_id.strip().lower().startswith('tt'):
            raise ValueError('Movie IMDb ID is invalid (should starts with tt).')
        else:
            self.imdb_id = self.imdb_id.strip().lower()
        # Ensure that movie run time is valid (normal).
        if self.run_time < 60 or self.run_time > 250:
            raise ValueError('Movie run time is out of range [60, 250].')
        # Resize the movie poster image, and convert the image to jpeg if necessary.
        if self.poster_data and self.poster_type:
            if images.Image(self.poster_data).width > 200:
                logging.info('Processing poster image for movie %s...' % self.imdb_id)
                self.poster_data = images.resize(
                    self.poster_data,
                    width=200,
                    output_encoding=images.JPEG
                )
                self.poster_type = 'image/jpeg'
        # Store the instance to datastore.
        return super(HotMovie, self).put()
    
    @staticmethod
    def get_by_imdb_id(imdb_id):
        return HotMovie.gql('WHERE imdb_id = :imdb_id', imdb_id=imdb_id).get()
    
    @staticmethod
    def get_hottest(num=10):
        query = HotMovie.all()
        query.order('-hot_seconds')
        return query.fetch(num)


#---------------------------------------------------------------------------------------------------

class YoutubeLink(object):

    def __init__(self, url):
        self.raw_url = url
        self.video_id = None
        self.youtube_url = None
        if url:
            try:
                url_parts = urlparse.urlsplit(url)
                server = url_parts[1].lower()
                path = url_parts[2]
                vparam = filter(lambda x: x.startswith('v='), url_parts[3].split('&'))
                if server == 'www.youtube.com' and path == '/watch' and len(vparam) == 1:
                    self.video_id = vparam[0][2:]
                    self.youtube_url = 'http://%s%s?v=%s' % (server, path, self.video_id)
            except:
                pass
    
    def is_valid(self):
        return bool(self.video_id and self.youtube_url)
    
    def embed_url(self):
        return 'http://www.youtube.com/v/%s' % self.video_id
    
    def __nonzero__(self):
        return self.is_valid()
    
    def __str__(self):
        return str(self.youtube_url)
    
    def __unicode__(self):
        return unicode(self.youtube_url)

#---------------------------------------------------------------------------------------------------

class HotScene(db.Model):

    movie_name = db.StringProperty()  # Auto-calculated property.
    start_time = db.IntegerProperty(required=True)
    seconds = db.IntegerProperty(required=True)
    description = db.TextProperty()
    video_url = db.LinkProperty()
    actress = db.StringProperty()
    actor = db.StringProperty()
    breast = db.BooleanProperty(default=False)
    hips = db.BooleanProperty(default=False)
    legs = db.BooleanProperty(default=False)
    confirmed = db.ListProperty(users.User, default=[])
    disproved = db.ListProperty(users.User, default=[])
    favored = db.ListProperty(users.User, default=[])
    anonymous_confirmed = db.ListProperty(users.User, default=[])  # TODO: to be removed.
    anonymous_disproved = db.ListProperty(users.User, default=[])  # TODO: to be removed.
    comments = db.IntegerProperty(default=0)
    submitter = db.UserProperty(required=True)
    submit_date = db.DateTimeProperty(auto_now_add=True)
    
    def youtube_link(self):
        return YoutubeLink(self.video_url)
    
    def current_user_opinion(self):
        user = users.get_current_user()
        if user in self.confirmed:
            return 'confirmed'
        elif user in self.disproved:
            return 'disproved'
        else:
            return None
    
    def is_editable(self):
        return (self.submitter == users.get_current_user())
    
    def is_deletable(self):
        return (self.is_editable() and not self.confirmed and not self.comments)
    
    def put(self):
        # Check the existance of the parent movie of this scene.
        if not self.parent():
            raise ValueError('Inconsistency found: HottyScene does not have a parent.')
        # Set movie name of this scene.
        self.movie_name = self.parent().name
        # Ensure that start time is consistent.
        if self.start_time <= 0 or self.start_time >= self.parent().run_time:
            raise ValueError('Scene start time is out of range [0, %d].' % self.parent().run_time)
        # Ensure that the scene lasts long enough.
        if self.seconds < 10:
            raise ValueError('Scene is not long enough (only %d seconds).' % self.seconds)
        # For optional string properties, strip spaces and set to None if empty.
        self.description = _strip_string(self.description)
        self.actress = _strip_string(self.actress)
        self.actor = _strip_string(self.actor)
        # If this scene is new, increment the hot scene counter of the parent movie.
        if not self.is_saved():
            self.parent().hot_scenes += 1
            self.parent().hot_seconds += self.seconds
            self.parent().put()
        # Put the entity to datastore.
        return super(HotScene, self).put()

#---------------------------------------------------------------------------------------------------

class HotUser(db.Model):
    """This class records a user's contribution to the website. The key_name of an entity is
    ('user_%s' % self.user.user_id()). Google's User instance has a numeric ID which cannot be used
    directly as the key_name of the datastore entities, so we add the 'user_' prefix to it.
    """
    
    user = db.UserProperty(required=True)
    full_name = db.StringProperty()
    sex = db.StringProperty()  # possible values are 'male' and 'female'.
    bio = db.TextProperty()
    photo_type = db.StringProperty()
    photo_data = db.BlobProperty()
    register_date = db.DateTimeProperty(auto_now_add=True)
    movies_added = db.IntegerProperty(default=0)
    scenes_added = db.IntegerProperty(default=0)
    scenes_voted = db.IntegerProperty(default=0)
    points = db.IntegerProperty(default=0)
    
    _MALE = 'male'
    _FEMALE = 'female'
    
    def __str__(self):
        return str(self.user)
    
    def __unicode__(self):
        return unicode(self.user)
    
    def user_id(self):
        return self.user.user_id()
    
    def nickname(self):
        return self.user.nickname()
    
    def email(self):
        return self.user.email()
    
    def has_photo(self):
        return bool(self.photo_type and self.photo_data)
    
    def he(self):
        HE_OR_SHE = {HotUser._MALE: 'he', HotUser._FEMALE: 'she',}
        return HE_OR_SHE.get(self.sex, 'she')
    
    def his(self):
        HIS_OR_HER = {HotUser._MALE: 'his', HotUser._FEMALE: 'her',}
        return HIS_OR_HER.get(self.sex, 'her')
    
    def him(self):
        HIM_OR_HER = {HotUser._MALE: 'him', HotUser._FEMALE: 'her',}
        return HIM_OR_HER.get(self.sex, 'her')
    
    def is_current_user(self):
        return (self.user == users.get_current_user())
    
    def put(self):
        # Calculate the contribution points of this user.
        self.points = self.movies_added * 2 + self.scenes_added * 3 + self.scenes_voted
        # Resize the user's photo image, and convert the image to jpeg if necessary.
        if self.photo_data and self.photo_type:
            if images.Image(self.photo_data).width > 200:
                logging.info('Processing photo image for user %s...' % self.user.nickname())
                self.photo_data = images.resize(
                    self.photo_data,
                    width=200,
                    output_encoding=images.JPEG
                )
                self.photo_type = 'image/jpeg'
        # Store the instance to datastore.
        return super(HotUser, self).put()
    
    @staticmethod
    def get_hottest(num=10):
        query = HotUser.all()
        query.order('-points')
        return query.fetch(num)










